#include "ExpectimaxNegamax.h"
#include <exception>
#include <cfloat>
#include "CommonApps.h"
//C'tor
//Expectimax::Expectimax(SenetHeuristics& heurisitic): m_heuristic(heurisitic){}

void ExpectimaxNegamax::Init(const SenetPlayerExt* owner , int gameStickNum)
{
	m_gameStickNum = gameStickNum;
	m_probabilities.init(gameStickNum);
	m_heuristic.init(owner , m_probabilities); 
	
}

// Assignment operator
//Expectimax& Expectimax::operator =(const Expectimax& e)
//{
//	m_heuristic = e.m_heuristic;
//	m_gameStickNum = e.m_gameStickNum;
//	return *this;
//}


//calculate negamax
void ExpectimaxNegamax::negamax(
						Score& res ,
						SenetBoardDraft& board,
						int depth,
						const GameTimer& timer,
						bool checkTime,
						Definitions::PlayerColor currentPlayer,
						int stickNum /* the number of sticks that current player got */,
						NodeType nodeType,
						float alpha,
						float beta)
{
	if(checkTime && timer.isMoveTimePassed(GameTimer::NO_OVERHEAD))
	{
		throw Exception::TIME_OUT;
	}
	res.pieceToMove = -1;
	if(board.isTerminal() || depth == 0)
	{
		res.score = m_heuristic.score(board, currentPlayer);
	}
	else
	{
		//TODO: Debug:
		alpha = CommonApps::floatMax(alpha, m_heuristic.getLowerHeuristicBound());
		beta = CommonApps::floatMin(beta, m_heuristic.getUpperHeuristicBound());


		// generate moves
		vector<int> moves;
		board.generateMoves(moves, currentPlayer, stickNum);
		int movesSize = moves.size();
		if(movesSize == 0)
		{
			res.score = m_heuristic.score(board, currentPlayer);
			return;
		}

		// for each child
		for(int i = 0 ;	i < movesSize;	++i)
		{
			int startLoc = moves[i];
			int endLoc = startLoc + stickNum;

			//do move
			vector<SenetBoardDraftMove> movesPerformed;
			board.doMove(movesPerformed, startLoc, endLoc, currentPlayer);

			//score move
			Score val = chanceSearch(
				board , depth-1 ,
				timer, checkTime ,
				Definitions::getOppositePlayer(currentPlayer),
				i , CHANCE, CommonApps::getOppositeNodeType(nodeType));
			val.score *= -1.0f;

			//undo move
			CommonApps::undoMoves(movesPerformed, board , currentPlayer);

			// update the current maximum score
			// the following is actually: current_max = max(current_max, val)
			if(val.score > alpha)
			{
				if (val.score >= beta)
				{
					//res.score = beta;
					res.score = val.score;
					res.pieceToMove = startLoc;
					return;
				}
				alpha = val.score;
				res.pieceToMove = startLoc;
			}
		}
		res.score = alpha;
	}	
}


//calculate value at chance nodes
void ExpectimaxNegamax::expectimax(Score& res, SenetBoardDraft &board, int depth, const GameTimer& timer, bool checkTime , 
						Definitions::PlayerColor nextPlayer, NodeType nextTurn) throw (Exception)
{
	if(checkTime && timer.isMoveTimePassed(GameTimer::NO_OVERHEAD))
	{
		throw Exception::TIME_OUT;
	}
	if(board.isTerminal() || depth == 0)
	{
		res.pieceToMove = -1;
		res.score = m_heuristic.score(board, nextPlayer);
	}
	else
	{
		//more resources to use - continue search
		float sum = 0;
		for(int i = 0; i <= m_gameStickNum; ++i)
		{
			Score val = chanceSearch(board, depth-1  , timer , checkTime , nextPlayer , i , nextTurn , DONT_CARE);
			sum += m_probabilities.getProbability(i) * val.score;		
		}
		res.score = sum;
		res.pieceToMove = -1; 
	}	
}


//driver function
Score ExpectimaxNegamax::chanceSearch(SenetBoardDraft& board, int depth, const GameTimer& timer, bool checkTime ,
						Definitions::PlayerColor currentPlayer, int stickNum ,  NodeType nodeType , NodeType nextTurn) throw(Exception)
{	
	Score res;
	if(nodeType == CHANCE)
	{
		expectimax(res , board, depth, timer, checkTime, currentPlayer, nextTurn);
	}
	else
	{
		//minimax(res , board, depth, timer, checkTime, currentPlayer, stickNum , nodeType);
		negamax(res , board, depth, timer, checkTime, currentPlayer, stickNum , nodeType, -FLT_MAX, FLT_MAX);
	}
	return res;
	
}


//main function - init turn parameters and calculation
SenetMove ExpectimaxNegamax::scoreExpectimax(SenetBoardDraft& board, int depth, const GameTimer& timer , 
									  bool checkTime , Definitions::PlayerColor myColor, int stickNum) throw(Exception)
{	
	//calculate score
	Score  res =  chanceSearch(board, depth, timer, checkTime , myColor, stickNum, MAX , DONT_CARE);

	m_FinalBoardScore = res.score;

	SenetMove resMove(res.pieceToMove);	
	return resMove;

}
